package com.zhouyu.spring;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 模拟 Spring Context 上下文
 */
public class ZhouyuApplicationContext {

	private Class configClass;

	private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

	private Map<String, Object> singleObjects = new ConcurrentHashMap<>();

	private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

	public ZhouyuApplicationContext(Class configClass) {
		this.configClass = configClass;

		scan(configClass);

		for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
			String beanName = entry.getKey();
			BeanDefinition beanDefinition = entry.getValue();
			if (beanDefinition.getScope().equals("singleton")) {
				Object bean = createBean(beanName, beanDefinition);
				singleObjects.put(beanName, bean);
			}

		}
	}


	void scan(Class configClass) {

		// 获取扫描包的配置
		if (configClass.isAnnotationPresent(ComponentScan.class)) {
			ComponentScan annotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
			String packageName = annotation.value();
			String path = packageName.replace(".", "/");
			ClassLoader classLoader = ZhouyuApplicationContext.class.getClassLoader();
			URL resource = classLoader.getResource(path);

			File file = new File(resource.getFile());

			List<File> classFile = new ArrayList<>();

			// 扫描所有的 Bean 文件 class
			if (file.isDirectory()) {
				for (File listFile : file.listFiles()) {
					if (listFile.isDirectory()) {
						for (File subFile : listFile.listFiles()) {
							if (!subFile.isDirectory()) {
								classFile.add(subFile);
							} else {
								// TODO: 2025/7/30 考虑递归
							}
						}
					} else {
						classFile.add(listFile);
					}
				}
			}

			for (File cFile : classFile) {
				String absolutePath = cFile.getAbsolutePath();
				String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
						.replace("/", ".");

				try {
					Class<?> clazz = classLoader.loadClass(className);

					if (clazz.isAnnotationPresent(Component.class)) {

						// 这里直接 newInstance 不考虑 属性注入么？
						if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
							BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getConstructor().newInstance();
							beanPostProcessorList.add(beanPostProcessor);
						}

						BeanDefinition beanDefinition = new BeanDefinition();
						beanDefinition.setType(clazz);
						beanDefinition.setLazy(clazz.isAnnotationPresent(Lazy.class));

						String beanName = clazz.getAnnotation(Component.class).value();
						if (beanName.isEmpty()) {
							beanName = Introspector.decapitalize(clazz.getSimpleName());
						}

						if (clazz.isAnnotationPresent(Scope.class)) {
							beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
						} else {
							beanDefinition.setScope("singleton");
						}

						beanDefinitionMap.put(beanName, beanDefinition);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	private Object createBean(String beanName, BeanDefinition beanDefinition) {
		Class clazz = beanDefinition.getType();
		try {
			Object instance = clazz.newInstance();

			// 属性注入，spring 官方加入二级缓存，这里仅做简单实现
			for (Field field : clazz.getDeclaredFields()) {
				if (field.isAnnotationPresent(Autowired.class)) {
					Object bean = getBean(field.getName());
					field.setAccessible(true);
					field.set(instance, bean);
				}
			}

			//
			if (instance instanceof BeanNameAware) {
				((BeanNameAware) instance).setBeanName(beanName);
			}

			// 前置处理
			for (BeanPostProcessor postProcessor : beanPostProcessorList) {
				instance = postProcessor.postProcessBeforeInitialization(instance, beanName);
			}

			if (instance instanceof InitializingBean) {
				((InitializingBean) instance).afterPropertiesSet();
			}

			if (instance instanceof ApplicationContextAware) {
				((ApplicationContextAware) instance).setApplicationContext(this);
			}

			// 后置处理
			for (BeanPostProcessor postProcessor : beanPostProcessorList) {
				instance = postProcessor.postProcessAfterInitialization(instance, beanName);
			}

			if (clazz.isAnnotationPresent(Transactional.class)) {
				Enhancer enhancer = new Enhancer();
				enhancer.setSuperclass(clazz);
				Object target = instance;
				enhancer.setCallback(new MethodInterceptor() {
					@Override
					public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
						System.out.println("开启事务");

						Object result = method.invoke(target, args);

						System.out.println("提交事务");

						return result;
					}
				});

				instance = enhancer.create();
			}

			return instance;

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public Object getBean(String beanName) {

		if (!beanDefinitionMap.containsKey(beanName)) {
			throw new NullPointerException("错误的 BeanName");
		}

		BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

		if (beanDefinition.getScope().equals("singleton")) {
			Object singletonBean = singleObjects.get(beanName);
			if (singletonBean == null) {
				singletonBean = createBean(beanName, beanDefinition);
				singleObjects.put(beanName, singletonBean);
			}
			return singletonBean;
		} else {
			// 原型 Bean，每次都要从新创建
			return createBean(beanName, beanDefinition);
		}
	}
}
